import 'dart:convert';
import 'dart:io';
import 'dart:math';

import 'package:connectivity/connectivity.dart';
import 'package:crypto/crypto.dart';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:package_info/package_info.dart';
import 'package:practice/constants/Constants.dart';
import 'package:practice/manager/user_manager.dart';
import 'package:practice/practice.dart';
import 'package:practice/toast_utils.dart';
import 'exception_reason.dart';
import 'net_error.dart';

class NetUtils {
  static const String GET = 'get';
  static const String POST = 'post';
  static const String PUT = 'put';
  static const String PATCH = 'patch';
  static const String DELETE = 'delete';

  static late Dio dio;

  // static PersistCookieJar cookieJar;
  static init() async {
    PackageInfo packageInfo = await PackageInfo.fromPlatform();
    dio = new Dio(BaseOptions(
      // baseUrl: URLConstants.API_URL,
      sendTimeout: 1000 * 20,
      receiveTimeout: 1000 * 20,
      headers: {'platform': Platform.operatingSystem, 'version': packageInfo.version, "loginUser": "A_"},
//      contentType: Headers.formUrlEncodedContentType,
      connectTimeout: 1000 * 20,
    ));
    (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
      client.badCertificateCallback = (cert, host, port) {
        return true;
      };
    };

    ///日志拦截
    dio.interceptors.add(InterceptorsWrapper(onRequest: (RequestOptions options, handler) {
      String? userId;
      String? clientId;
      userId = UserManager.user.id;
      clientId = UserManager.user.clientId;

      options.headers['platform'] = Platform.operatingSystem;
      options.headers['version'] = packageInfo.version;
      options.headers['loginUser'] = "A_${userId ?? ""}";
      if (clientId != null) {
        options.headers['clientId'] = clientId;
      }
      StringBuffer sb = StringBuffer();
      sb.write('request path=${options.path}');
      sb.write('\n');
      sb.write('request method=${options.method}');
      sb.write('\n');
      sb.write('request headers=${options.headers}');
      sb.write('\n');
      sb.write(
          'request data=${options.data is FormData ? (options.data as FormData).fields.toString() : options.data.toString()}');
      sb.write('\n');
      sb.write('request queryParameters=${options.queryParameters}');
      debugPrint(sb.toString());
      // Log.d(sb.toString(), true);
      return handler.next(options);
    }, onResponse: (Response response, handler) {
      StringBuffer sb = StringBuffer();
      sb.write('response path= ${response.requestOptions.path}');
      sb.write('\n');
      sb.write('response data=${jsonEncode(response.data)}');
      debugPrint(sb.toString());
      // Log.i(sb.toString(), true);
      if (!(response.data is String)) {
        var code = response.data['code'];
        if (code == 50001 || code == 50002 || code == 50003) {
          Practice.onResult(PracticeAction.reLogin);
          return;
        }
      }

      return handler.next(response);
    }, onError: (DioError e, handler) {
      return handler.next(e);
    }));
  }

  ///去空value 排序 签名
  static Map<String, dynamic> decoratorData(Map<String, dynamic>? data, bool isSign) {
    if (data == null) {
      data = Map<String, dynamic>();
    }
//    debugPrint("原始数据");
//    debugPrint(parameter.toString());
    data["uid"] = UserManager.user.id; //用户ID 18位id
    data["nonce"] = Random().nextInt(999999).toString(); //随机数字
    data["timestamp"] = DateTime.now().toString();
    data["orgId"] =
        UserManager.organ.institutionId; //机构id

//    debugPrint("去掉空值");
//     data.removeNull();
    if (isSign) {
      data["sign"] = getSign(data);
    }
    return data;
  }


  /// course服务请求
  static Future practiceRequest(
    String url, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    String method = GET,
    bool sign = true,
    bool isBody = false,
  }) {
    return _request(
      "${Constants.debug ? Constants.API_URL_PRACTICE_DEBUG : Constants.API_URL_PRACTICE}$url",
      data: data,
      queryParameters: queryParameters,
      method: method,
      sign: sign,
      isBody: isBody,
    );
  }

  ///网络请求
  ///[url] 请求地址
  /// [isBody] 请求默认使用formdata,如果接口需要body请求,设置为true
  static Future _request(
    String url, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    String method = GET,
    bool sign = true,
    bool isBody = false,
    ProgressCallback? onSendProgress,
  }) async {
    if (data is Map) {
      data.remove("version");
      data.remove("createTime");
      data.remove("updateTime");
      data.remove("modifyTime");
      data.remove("deleted");
    }
    if (queryParameters != null) {
      queryParameters.remove("version");
      queryParameters.remove("createTime");
      queryParameters.remove("updateTime");
      queryParameters.remove("modifyTime");
      queryParameters.remove("deleted");
    }

    var endData = data is Map<String, dynamic> ? decoratorData(data, sign) : data;
    // debugPrint("data=${jsonEncode(endData)}");
    var response = await dio
        .request(url,
            data: data == null
                ? null
                : isBody
                    ? endData
                    : FormData.fromMap(endData),
            queryParameters: queryParameters == null ? null : decoratorData(queryParameters, sign),
            options: Options(method: method),
            onSendProgress: onSendProgress)
        .then((response) {
      if (response.statusCode != HttpStatus.ok && response.statusCode != HttpStatus.created) {
        //非200抛出异常
        String statusCode = Constants.debug ? '[${response.statusCode}]' : "";
        throw NetError(
            errCode: ExceptionReason.RESPONSE,
            errMsg: "${ExceptionReason.getErrMsg(ExceptionReason.RESPONSE)}$statusCode");
      }

      if (response.data is Map) {
        if (response.data['code'] != 200) {
          ToastUtils.showToast(msg: response.data['msg'] ?? "请求异常[${response.data['code']}]");
          throw NetError(errCode: response.data['code'], errMsg: response.data['msg'] ?? "请求异常");
        }
          return response.data;
      } else if (response.data is List) {
        return response.data;
      } else {
        //返回数据非Map 走异常, 以后如果有其他格式数据再修改
        throw NetError(
            errCode: ExceptionReason.DATA_ERROR, errMsg: ExceptionReason.getErrMsg(ExceptionReason.DATA_ERROR));
      }
    }).catchError((onError) {
      // StringBuffer sb = StringBuffer();
      // sb.write("错误接口：${url}");
      // sb.write("\n");
      // sb.write(onError.toString());
      // Log.w(sb.toString());
      if (onError is NetError) {
        throw onError;
      }
      throw getError(onError);
    });
    return response;
  }


  static NetError getError(DioError error) {
    NetError netError;
    switch (error.type) {
      case DioErrorType.connectTimeout:
        netError = NetError(
            errCode: ExceptionReason.CONNECT_TIMEOUT,
            errMsg: ExceptionReason.getErrMsg(ExceptionReason.CONNECT_TIMEOUT));
        break;
      case DioErrorType.sendTimeout:
        netError = NetError(
            errCode: ExceptionReason.SEND_TIMEOUT, errMsg: ExceptionReason.getErrMsg(ExceptionReason.SEND_TIMEOUT));
        break;
      case DioErrorType.receiveTimeout:
        netError = NetError(
            errCode: ExceptionReason.RECEIVE_TIMEOUT,
            errMsg: ExceptionReason.getErrMsg(ExceptionReason.RECEIVE_TIMEOUT));
        break;
      case DioErrorType.response:
        netError =
            NetError(errCode: ExceptionReason.RESPONSE, errMsg: ExceptionReason.getErrMsg(ExceptionReason.RESPONSE));
        break;
      case DioErrorType.cancel:
        netError = NetError(errCode: ExceptionReason.CANCEL, errMsg: ExceptionReason.getErrMsg(ExceptionReason.CANCEL));
        break;
      case DioErrorType.other:
        dynamic ex = error.error;
        if (ex is SocketException) {
          netError = NetError(errCode: ExceptionReason.DEFAULT, errMsg: "连接异常,请检查网络");
        } else if (ex is HttpException) {
          netError = NetError(errCode: ExceptionReason.DEFAULT, errMsg: "网络错误");
        } else {
          netError =
              NetError(errCode: ExceptionReason.DEFAULT, errMsg: ExceptionReason.getErrMsg(ExceptionReason.DEFAULT));
        }
        break;
    }
    ToastUtils.showToast(msg: netError.errMsg);
    try {
      print(error.toString());
      print(error.response?.data?.toString());
    } catch (error) {}

    return netError;
  }

  static void cancelRequest(CancelToken token) {
    token.cancel();
  }

  ///网络是否可用
  static Future<bool> isNetWorkAvailable() async {
    var connectivityResult = await (Connectivity().checkConnectivity());
    if (connectivityResult == ConnectivityResult.none) {
      return false;
    } else {
      return true;
    }
  }

  ///网络类型
  ///移动网络：[ConnectivityResult.mobile]
  ///WIFI网络：[ConnectivityResult.wifi]
  ///无网络网络：[ConnectivityResult.none]
  static Future<ConnectivityResult> getNetWorkType() async {
    var connectivityResult = await (Connectivity().checkConnectivity());
    return connectivityResult;
  }

  static String getSign(Map parameter) {
    String priKey = UserManager.organ.privateKey!;
    // if (priKey == null) {
    //   print("priKey is null");
    //   return null;
    // }
//    debugPrint("拼接key=value&");
    /// 拼接key=value&
    List<String> allKeys = [];
    parameter.forEach((key, value) {
      allKeys.add("$key=$value&");
    });
//    debugPrint(allKeys.toString());
//    debugPrint("排序");
    /// key排序
    allKeys.sort((obj1, obj2) {
      return obj1.compareTo(obj2);
    });
//    debugPrint(allKeys.toString());
    /// 数组转string
    String pairsString = allKeys.join("");
//    debugPrint("拼接字符串");
    /// 拼接 key
    String signString = "${pairsString}priKey=$priKey";
//    debugPrint(signString);
//    debugPrint("生成md5");
    String sign = generateMd5(signString);
//    debugPrint(sign);
    return sign;
  }

  /// md5加密
  static String generateMd5(String data) {
    var content = new Utf8Encoder().convert(data);
    var digest = md5.convert(content);
    return digest.toString();
  }
}
